using Unity.Burst;                      // Burst 编译器，用于高性能 Job 编译
using Unity.Collections;                // 提供 NativeArray、ComputeBuffer 等高性能数据结构
using Unity.Jobs;                       // Unity Job System，用于多线程任务调度
using Unity.Mathematics;                // 提供 float3、quaternion 等数学类型
using UnityEngine;                      // Unity 基础功能

// 引入静态数学函数，简化书写
using static Unity.Mathematics.math;
// 为清晰，使用 Unity.Mathematics 的 quaternion 类型
using quaternion = Unity.Mathematics.quaternion;
// 使用 Unity 的 Random，而不是 System.Random
using Random = UnityEngine.Random;

public class Fractal : MonoBehaviour
{
	// ===========================
	// 【Job 定义】并行更新分形每一层的逻辑
	// 使用 IJobFor，对每个分形部件（索引 i）进行并行计算
	// ===========================

	[BurstCompile(FloatPrecision.Standard, FloatMode.Fast, CompileSynchronously = true)]
	struct UpdateFractalLevelJob : IJobFor
	{
		// 当前层级的缩放系数（每下一层缩小一半）
		public float scale;

		// 帧间隔时间，用于旋转速度计算
		public float deltaTime;

		// 【只读】父级部件数组，用于获取父级旋转与位置
		[ReadOnly]
		public NativeArray<FractalPart> parents;

		// 【读写】当前层级的部件数组，存储每个子部件的状态
		public NativeArray<FractalPart> parts;

		// 【只写】输出的变换矩阵数组，用于 GPU Instancing 渲染
		[WriteOnly]
		public NativeArray<float3x4> matrices;

		// 每个分形部件的执行逻辑（每个索引 i 对应一个子部件）
		public void Execute(int i)
		{
			// 获取当前部件的父级：因为每父级有 5 个子部件，所以父级索引为 i / 5
			FractalPart parent = parents[i / 5];

			// 获取当前子部件
			FractalPart part = parts[i];

			// 更新当前部件的自旋角度（基于旋转速度与帧时间）
			part.spinAngle += part.spinVelocity * deltaTime;

			// ======================
			// 下面的代码尝试计算一个“下垂”或“弯曲”的效果（sag）
			// 目的是让子部件不是完全垂直于父级，而是有一定的自然弯曲
			// ======================

			// 计算“上方向”：先应用父级旋转，再应用子部件自身旋转，再绕世界 Y 轴旋转
			// （可能意图是得到一个“理论上的正直向上方向”）
			float3 upAxis = mul(mul(parent.worldRotation, part.rotation), up());

			// 计算与上方向的叉积，得到一个“侧向/弯曲方向”
			float3 sagAxis = cross(up(), upAxis);

			// 计算该侧向向量的长度，如果几乎为 0，说明没有弯曲
			float sagMagnitude = length(sagAxis);

			quaternion baseRotation; // 用于存储基础旋转（可能包含弯曲）

			if (sagMagnitude > 0f)
			{
				// 如果存在明显的弯曲方向，则对该方向进行归一化
				sagAxis /= sagMagnitude;

				// 根据弯曲方向与最大弯曲角度，构造一个绕该轴的旋转四元数
				// 弯曲角度的大小也与 sagMagnitude 成正比（可能是为了模拟自然变形）
				quaternion sagRotation =
					quaternion.AxisAngle(sagAxis, part.maxSagAngle * sagMagnitude);

				// 最终的基础旋转 = 弯曲旋转 × 父级旋转
				baseRotation = mul(sagRotation, parent.worldRotation);
			}
			else
			{
				// 如果没有明显弯曲，则直接使用父级旋转作为基础
				baseRotation = parent.worldRotation;
			}

			// 最终的世界旋转：
			// 先应用基础旋转，再应用子部件自身旋转，再叠加绕 Y 轴的动态自旋
			part.worldRotation = mul(baseRotation,
				mul(part.rotation, quaternion.RotateY(part.spinAngle))
			);

			// 最终的世界位置：
			// 在父级位置的基础上，沿着旋转后的“上方向”偏移一定距离（1.5倍缩放）
			part.worldPosition =
				parent.worldPosition +
				mul(part.worldRotation, float3(0f, 1.5f * scale, 0f));

			// 保存更新后的部件数据
			parts[i] = part;

			// 构造用于 GPU 渲染的变换矩阵 float3x4：
			// 包括旋转矩阵（3x3）和位移向量（位置）
			float3x3 r = float3x3(part.worldRotation) * scale; // 旋转并应用缩放
			matrices[i] = float3x4(r.c0, r.c1, r.c2, part.worldPosition); // 组合成 float3x4
		}
	}

	// ===========================
	// 【数据结构】每个分形部件的数据
	// ===========================

	struct FractalPart
	{
		// 世界空间中的位置
		public float3 worldPosition;
		// 部件的旋转（局部或初始）
		public quaternion rotation, worldRotation;
		// 最大弯曲角度、当前自旋角度、自旋速度（弧度制）
		public float maxSagAngle, spinAngle, spinVelocity;
	}

	// ===========================
	// 【Shader 相关常量】属性 ID，用于高效设置 MaterialPropertyBlock
	// ===========================

	static readonly int
		colorAId = Shader.PropertyToID("_ColorA"),            // 颜色 A 的属性 ID
		colorBId = Shader.PropertyToID("_ColorB"),            // 颜色 B 的属性 ID
		matricesId = Shader.PropertyToID("_Matrices"),        // 变换矩阵缓冲区的属性 ID
		sequenceNumbersId = Shader.PropertyToID("_SequenceNumbers"); // 序列号（可能用于随机化）


	// ===========================
	// 【静态旋转数组】每个子部件的初始旋转方向（上、右、左、前、后）
	// ===========================
	static quaternion[] rotations = {
		quaternion.identity,                                  // 无旋转
        quaternion.RotateZ(-0.5f * PI),                       // 绕 Z 轴左转 90°
        quaternion.RotateZ(0.5f * PI),                        // 绕 Z 轴右转 90°
        quaternion.RotateX(0.5f * PI),                        // 绕 X 轴上转 90°
        quaternion.RotateX(-0.5f * PI)                        // 绕 X 轴下转 90°
    };


	// ===========================
	// 【材质属性控制块】用于高效设置渲染参数
	// ===========================
	static MaterialPropertyBlock propertyBlock;


	// ===========================
	// 【序列化字段】可在 Inspector 中调节的分形参数
	// ===========================
	[SerializeField, Range(3, 8)] int depth = 4;              // 分形深度（层数）：3 ~ 8
	[SerializeField] Mesh mesh, leafMesh;                     // 普通部件与叶子部件的网格
	[SerializeField] Material material;                       // 渲染材质
	[SerializeField] Gradient gradientA, gradientB;           // 渐变色 A 和 B（用于非叶子部件）
	[SerializeField] Color leafColorA, leafColorB;            // 叶子部件的颜色 A 和 B
	[SerializeField, Range(0f, 90f)] float maxSagAngleA = 15f, maxSagAngleB = 25f; // 最大弯曲角度范围
	[SerializeField, Range(0f, 90f)] float spinSpeedA = 20f, spinSpeedB = 25f;     // 自旋速度范围
	[SerializeField, Range(0f, 1f)] float reverseSpinChance = 0.25f; // 反向旋转概率

	// ===========================
	// 【运行时数据结构】分形部件与矩阵的存储
	// 使用 NativeArray 和 ComputeBuffer 实现高性能并行与 GPU 传递
	// ===========================

	NativeArray<FractalPart>[] parts;         // 每一层的分形部件数组
	NativeArray<float3x4>[] matrices;         // 每一层的变换矩阵数组
	ComputeBuffer[] matricesBuffers;          // 每一层矩阵的 GPU 缓冲区
	Vector4[] sequenceNumbers;                // 每一层的随机序列号（可能用于 Shader 随机化）

	// ===========================
	// 【生命周期方法】
	// ===========================

	void OnEnable()
	{
		// 初始化每一层的数据容器
		parts = new NativeArray<FractalPart>[depth];
		matrices = new NativeArray<float3x4>[depth];
		matricesBuffers = new ComputeBuffer[depth];
		sequenceNumbers = new Vector4[depth];

		int stride = 12 * 4; // 每个 float3x4 占 12 个 float，每个 float 4 字节 → 48 字节？可能需核对
		for (int i = 0, length = 1; i < parts.Length; i++, length *= 5)
		{
			// 每一层部件数量为 5^i，比如第0层1个，第1层5个，第2层25个...
			parts[i] = new NativeArray<FractalPart>(length, Allocator.Persistent);
			matrices[i] = new NativeArray<float3x4>(length, Allocator.Persistent);
			matricesBuffers[i] = new ComputeBuffer(length, stride);

			// 每层随机初始化一个序列号（可能用于 Shader 中的随机效果）
			sequenceNumbers[i] = new Vector4(Random.value, Random.value, Random.value, Random.value);
		}

		// 初始化根节点（第0层第0个部件）
		parts[0][0] = CreatePart(0);

		// 初始化其它所有部件（从第1层开始，每层5个子部件对应一个父级）
		for (int li = 1; li < parts.Length; li++)
		{
			NativeArray<FractalPart> levelParts = parts[li];
			for (int fpi = 0; fpi < levelParts.Length; fpi += 5)
			{
				for (int ci = 0; ci < 5; ci++)
				{
					levelParts[fpi + ci] = CreatePart(ci);
				}
			}
		}

		// 初始化 MaterialPropertyBlock（用于高效设置 Shader 参数）
		propertyBlock ??= new MaterialPropertyBlock();
	}

	void OnDisable()
	{
		// 释放所有 GPU 缓冲区与 NativeArray，防止内存泄漏
		for (int i = 0; i < matricesBuffers.Length; i++)
		{
			if (matricesBuffers[i] != null) matricesBuffers[i].Release();
			if (parts[i] != null) parts[i].Dispose();
			if (matrices[i] != null) matrices[i].Dispose();
		}
		// 清空引用
		parts = null;
		matrices = null;
		matricesBuffers = null;
		sequenceNumbers = null;
	}

	void OnValidate()
	{
		// 如果在 Inspector 修改了参数且当前是启用状态，则重新初始化
		if (parts != null && enabled)
		{
			OnDisable();
			OnEnable();
		}
	}


	// ===========================
	// 【工具方法】创建一个分形部件，并随机初始化其属性
	// ===========================
	FractalPart CreatePart(int childIndex)
	{
		return new FractalPart
		{
			// 最大弯曲角度：在两个预设值之间随机，并转为弧度
			maxSagAngle = radians(Random.Range(maxSagAngleA, maxSagAngleB)),

			// 初始旋转方向：根据子部件索引从预定义旋转数组中选取
			rotation = rotations[childIndex],

			// 自旋速度：随机范围，并可能反向，转为弧度
			spinVelocity = (Random.value < reverseSpinChance ? -1f : 1f) *
						   radians(Random.Range(spinSpeedA, spinSpeedB))
		};
	}


	// ===========================
	// 【每帧更新】主逻辑：更新分形变换并渲染
	// ===========================
	void Update()
	{
		float deltaTime = Time.deltaTime;

		// 更新根节点（第0层第0个部件）
		FractalPart rootPart = parts[0][0];
		rootPart.spinAngle += rootPart.spinVelocity * deltaTime;
		// 根节点的世界旋转 = 场景旋转 × 部件自身旋转 × Y轴自旋
		rootPart.worldRotation = mul(transform.rotation,
			mul(rootPart.rotation, quaternion.RotateY(rootPart.spinAngle))
		);
		// 根节点位置就是当前物体的位置
		rootPart.worldPosition = transform.position;
		parts[0][0] = rootPart;

		// 获取当前物体的缩放值
		float objectScale = transform.lossyScale.x;

		// 构造根节点的变换矩阵
		float3x3 r = float3x3(rootPart.worldRotation) * objectScale;
		matrices[0][0] = float3x4(r.c0, r.c1, r.c2, rootPart.worldPosition);

		// 当前层级的缩放值（每下一层缩小一半）
		float scale = objectScale;
		JobHandle jobHandle = default;

		// 从第1层开始，逐层并行更新部件旋转、位置和矩阵
		for (int li = 1; li < parts.Length; li++)
		{
			scale *= 0.5f; // 每层缩放减半

			// 调度一个并行 Job，更新该层的所有部件
			jobHandle = new UpdateFractalLevelJob
			{
				deltaTime = deltaTime,
				scale = scale,
				parents = parts[li - 1], // 父级是上一层
				parts = parts[li],       // 当前层
				matrices = matrices[li]  // 输出矩阵
			}.ScheduleParallel(parts[li].Length, 5, jobHandle); // 并行度为5
		}

		// 等待所有并行任务完成
		jobHandle.Complete();

		// 定义一个包围盒，用于 GPU 实例化绘制的视锥剔除
		var bounds = new Bounds(rootPart.worldPosition, 3f * objectScale * Vector3.one);

		// 渲染每一层的部件
		int leafIndex = matricesBuffers.Length - 1; // 假设最后一层是叶子
		for (int i = 0; i < matricesBuffers.Length; i++)
		{
			ComputeBuffer buffer = matricesBuffers[i];
			buffer.SetData(matrices[i]); // 将 CPU 数据拷贝到 GPU

			Color colorA, colorB;
			Mesh instanceMesh;

			if (i == leafIndex)
			{
				// 如果是最后一层（叶子层），使用叶子颜色与叶子网格
				colorA = leafColorA;
				colorB = leafColorB;
				instanceMesh = leafMesh;
			}
			else
			{
				// 否则使用普通颜色渐变与普通网格
				float gradientInterpolator = i / (float)(matricesBuffers.Length - 2);
				colorA = gradientA.Evaluate(gradientInterpolator);
				colorB = gradientB.Evaluate(gradientInterpolator);
				instanceMesh = mesh;
			}

			// 设置 MaterialPropertyBlock 中的各种参数
			propertyBlock.SetColor(colorAId, colorA);
			propertyBlock.SetColor(colorBId, colorB);
			propertyBlock.SetBuffer(matricesId, buffer);
			propertyBlock.SetVector(sequenceNumbersId, sequenceNumbers[i]);

			// 使用 GPU Instancing 绘制该层所有实例
			Graphics.DrawMeshInstancedProcedural(
				instanceMesh, 0, material, bounds, buffer.count, propertyBlock
			);
		}
	}
}